#ifndef CHESSBOARD_H_
#define CHESSBOARD_H_

#include <utility>
#include <iostream>

#include <boost/bind.hpp>

#include "graph_support/Graph.h"
#include "graph_support/attributes/LengthEdgeAttributes.h"

#include "graph_support/utils/PtrAccessOpSimulator.h"


// TODO 4 absence of namespace test_gs_fakes is workaround for boost::lexical_cast<>
//namespace test_gs_fakes
//{

class ChessBoard : public GRAPH_SUPPORT_NS::Graph
{
public:
	class GrVertex : public std::pair< int, int >
	{
	public:
		GrVertex( int col = 0, int row = 0 );
		bool operator == ( const GrVertex & that ) const;
	};

	typedef GRAPH_SUPPORT_NS::attributes::Length1EdgeAttributes EdgeAttributes;

	class GrEdge : public GRAPH_SUPPORT_NS::Graph::GrEdge
	{
	public:
		GrEdge( ChessBoard & cb, const GrVertex & edgBeg, const GrVertex & edgEnd );
		GrEdge();

	public:
		const GrVertex beg();
		const GrVertex end();
		size_t begId();
		size_t endId();
		EdgeAttributes & getAttributes();

	public:
		bool operator == ( const GrEdge & that ) const;

	private:
		GrVertex edgBeg;
		GrVertex edgEnd;
		EdgeAttributes attributes;
		ChessBoard * cb;
	};

	typedef GRAPH_SUPPORT_NS::utils::PtrAccessOpSimulator< GrEdge > GrEdgePtr;

	class VertexIterator : public GRAPH_SUPPORT_NS::Graph::VertexIterator
	{
	public:
		typedef int difference_type;
		typedef GrVertex value_type;
		typedef GrVertex * pointer;
		typedef GrVertex & reference;
		typedef std::forward_iterator_tag iterator_category;

	public:
		VertexIterator( const ChessBoard & chb, bool isEnd = false );

	public:
		const VertexIterator & operator ++ ();
		bool operator != ( const VertexIterator & that ) const;
		const GrVertex operator * ();
		GRAPH_SUPPORT_NS::utils::PtrAccessOpSimulator< GrVertex > operator -> ();

	private:
		const ChessBoard & chb;
		int curVert;
	};

	class EdgeIterator : public GRAPH_SUPPORT_NS::Graph::EdgeIterator
	{
	public:
		typedef int difference_type;
		typedef GrEdgePtr value_type;
		typedef GrEdgePtr * pointer;
		typedef GrEdgePtr & reference;
		typedef std::forward_iterator_tag iterator_category;

	public:
		EdgeIterator( ChessBoard & chb, int c, int r, bool isEnd = false );
		const EdgeIterator & operator ++ ();
		bool operator != ( const EdgeIterator & that ) const;
		bool operator == ( const EdgeIterator & that ) const;
		GrEdgePtr operator * ();
		GrEdgePtr operator -> ();

	private:
		void shpos2good();

	private:
		ChessBoard * chb;
		int r;
		int c;
		int shStepPos;
	};

public:
	ChessBoard( size_t rows, size_t columns );

public:
	size_t getVertexCount() const;
	VertexIterator getVerticesBeg();
	VertexIterator getVerticesEnd();
	EdgeIterator getEdgsBeg( const GrVertex & v );
	EdgeIterator getEdgsEnd( const GrVertex & v );
	EdgeIterator getEdgsBegId( size_t vid );
	EdgeIterator getEdgsEndId( size_t vid );

	template< typename FnT >
	void forEachOutEdge( GrVertex v, const FnT & fn )
	{
		forEachOutEdgeId( vert2num( v ), fn );
	}

	template< typename FnT >
	void forEachOutEdgeId( size_t vid, const FnT & fn )
	{
		// TODO: proper implementation
		std::for_each( getEdgsBegId( vid ), getEdgsEndId( vid ),
				boost::bind( fn, boost::bind( & GRAPH_SUPPORT_NS::utils::ptr2ref< GrEdge, GrEdgePtr >, _1 ) ) );
	}

public:
	size_t vert2num( const GrVertex & v ) const;
	const GrVertex num2vert( size_t vid ) const;

private:
	size_t rows;
	size_t columns;

	bool areCoordinatesGood( int c, int r );

private:
	/**
	 * delta steps
	 */
	static const int DELTA_STEPS_C[];
	static const int DELTA_STEPS_R[];
	static const int DSTEPS_COUNT = 8;
};

//}

std::iostream & operator << ( std::iostream & ostr, const /*test_gs_fakes::*/ChessBoard::GrVertex & vert );

int computeKnightOutcomes( int rows, int columns );

#endif /* CHESSBOARD_H_ */
